/****************************************************************
 * Fulguro
 * Copyright (C) 2004 Christophe Clienti
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 ***************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <flgrCoreMalloc.h>
#include <flgrCoreCopy.h>
#include <flgrCoreChannel.h>
#include <flgrCoreDataIO.h>
#include <flgrCoreMalloc.h>
#include <flgrCoreSystem.h>
#include <flgrCoreVector.h>
#include <flgrCoreDispatch.h>

#include "flgrImageIO.h"


#define FLGR_IMAGE_IO_BMP_RW    "BMP  : Read only"

#define FLGR_IMAGE_IO_PGM_RW    "PGM  : Read/Write"

#define FLGR_IMAGE_IO_RAW_RW    "RAW  : Read/Write"

#define FLGR_IMAGE_IO_TEXT_RW   "TEXT : Read/Write"



#ifdef LIB_PNG_SUPPORT

#define FLGR_IMAGE_IO_PNG_RW    "PNG  : Read/Write"

#include <png.h>
#include <setjmp.h>

#else
#define FLGR_IMAGE_IO_PNG_RW    "PNG  : No support"
#endif




#ifdef LIB_JPEG_SUPPORT

#define FLGR_IMAGE_IO_JPEG_RW    "JPEG : Read/Write"

#include <jpeglib.h>
#include <setjmp.h>

#else
#define FLGR_IMAGE_IO_JPEG_RW    "JPEG : No support"
#endif




#ifdef LIB_TIFF_SUPPORT

#define FLGR_IMAGE_IO_TIFF_RW  "TIFF : Read only"

#include <tiffio.h>

#else
#define FLGR_IMAGE_IO_TIFF_RW   "TIFF : No support"
#endif


#define FLGR_IMAGE_IO_SUPPORT			\
  FLGR_IMAGE_IO_PGM_RW				\
  "\n"						\
  FLGR_IMAGE_IO_PNG_RW				\
  "\n"						\
  FLGR_IMAGE_IO_BMP_RW				\
  "\n"						\
  FLGR_IMAGE_IO_JPEG_RW				\
  "\n"						\
  FLGR_IMAGE_IO_TIFF_RW				\
  "\n"						\
  FLGR_IMAGE_IO_RAW_RW				\
  "\n"						\
  FLGR_IMAGE_IO_TEXT_RW
  

/*!
 * \defgroup group_fulguro_image Image manipulation
 * \defgroup group_fulguro_image_io Input/Ouput
 * \ingroup group_fulguro_image
 * Functions to read or write images
 * @{
 */



int flgr_check_extension(char *filename, char *extToCheck) {
  FLGR_DECLARE_FUNCTION;
  
  int len_ext = strlen(extToCheck);
  int len=strlen(filename)-len_ext;
  char *fname = &(filename[len]);

  return (strcmp(fname,extToCheck)==0);
}


char *flgr_set_extension(char *filename, char *ext) {
  FLGR_DECLARE_FUNCTION;
  
  int len_ext = strlen(ext);
  int len = strlen(filename);
  char *fname = (char *) flgr_malloc(sizeof(char)*(len+1+len_ext));

  strcpy(fname,filename);
  strcat(fname,ext);

  return fname;
}

//////////////////////////////////////////////////////////////////
/*! Get supported image file format
 *  @returns a pointer to global const string
 */
//////////////////////////////////////////////////////////////////
char *flgr_get_supported_image_format(void) {
  return FLGR_IMAGE_IO_SUPPORT;
}

/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** PNG SUPPORT LOAD/SAVE
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/

//////////////////////////////////////////////////////////////////
/*! Load a Gray level PNG Image File
 *  @param filename : String for png file name
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_png(char *filename) {
#ifdef LIB_PNG_SUPPORT

  FLGR_Data2D *img;
  png_uint_32 size_x, size_y;
  png_byte magic[8];
  png_structp png_ptr;
  png_infop info_ptr;
  png_infop end_info;
  png_bytep *row_pointers = NULL;
  int bit_depth, color_type;
  FILE *fp = NULL;
  int i;
  int img_spp;
  FLGR_Type img_type;

  FLGR_DECLARE_FUNCTION;
  

  /* open image file */
  fp = fopen (filename, "rb");
  if (!fp) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  /* read magic number */
  fread (magic, 1, sizeof (magic), fp);

  /* check for valid magic number */
  if (!png_check_sig (magic, sizeof (magic))) {
    //POST_ERROR("\"%s\" is not a valid PNG image!\n",filename);
    fclose (fp);
    return NULL;
  }

  /* create a png read struct */
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    POST_ERROR("Could not allocate png structure\n");
    fclose (fp);
    return NULL;
  }

  /* create a png info struct */
  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    POST_ERROR("Could not create info structure\n");
    fclose (fp);
    png_destroy_read_struct (&png_ptr, NULL, NULL);
    return NULL;
  }

  end_info = png_create_info_struct(png_ptr);
  if (!end_info) {
    POST_ERROR("Could not create end info structure\n");
    png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
    fclose (fp);
    return NULL;
  }



  if (setjmp(png_jmpbuf(png_ptr))) {
    POST_ERROR("Error during init_io\n");
    png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
    fclose (fp);
    return NULL;
  }

  /* setup libpng for using standard C fread() function
     with our FILE pointer */
  png_init_io (png_ptr, fp);

  /* tell libpng that we have already read the magic number */
  png_set_sig_bytes (png_ptr, sizeof (magic));

  /* read png info */
  png_read_info (png_ptr, info_ptr);

  /* get some usefull information from header */
  bit_depth = png_get_bit_depth (png_ptr, info_ptr);
  color_type = png_get_color_type (png_ptr, info_ptr);

/*   if((color_type!=PNG_COLOR_TYPE_GRAY) && (color_type!=PNG_COLOR_TYPE_GRAY_ALPHA)) { */
/*     POST_ERROR("\"%s\" is a color image. This function loads only gray levels images!\n",filename); */
/*     png_destroy_read_struct(&png_ptr, &info_ptr,&end_info); */
/*     fclose (fp); */
/*     return NULL; */
/*   } */

  /* image with color palette transformed to rgb image*/
  if(color_type == PNG_COLOR_TYPE_PALETTE)
    png_set_palette_to_rgb(png_ptr);
  
  /* if gray bps<8 set to 8 */
  if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) 
    png_set_gray_1_2_4_to_8(png_ptr);
  
  /* transparency coded in alpha channel*/
  if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) 
    png_set_tRNS_to_alpha(png_ptr);

  /* Do not support 16 bits RGB(A) */
/*   if((bit_depth == 16) && ((color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA))) */
/*     png_set_strip_16(png_ptr); */

  /* Generate RGBA with A=0xFF */
/*   if (color_type == PNG_COLOR_TYPE_RGB) */
/*     png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); */

  /* update info structure to apply transformations */
  png_read_update_info (png_ptr, info_ptr);

  /* retrieve updated information */
  png_get_IHDR(png_ptr, info_ptr,
	       (png_uint_32*)(&size_x),
	       (png_uint_32*)(&size_y),
	       &bit_depth, &color_type,
	       NULL, NULL, NULL);


  switch(bit_depth) {
  case  8 : img_type=FLGR_UINT8;  break;
  case 16 : img_type=FLGR_UINT16; break;
  default:
    POST_ERROR("\"%s\" dont have standard gray levels : 1,2,4,8,16!\n",filename);
    png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
    fclose (fp);
    return NULL;
  }

  switch(color_type) {
  case PNG_COLOR_TYPE_GRAY       : img_spp=1; break;
  case PNG_COLOR_TYPE_RGB        : img_spp=3; break;
  case PNG_COLOR_TYPE_RGB_ALPHA  : img_spp=4; break;
  default:
    POST_ERROR("\"%s\" dont have standard gray levels : 1,2,4,8,16!\n",filename);
    png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
    fclose (fp);
    return NULL;
  }

  img = flgr2d_create_pixmap(size_y,size_x,img_spp,img_type);


  row_pointers = (png_bytep*) flgr_malloc(sizeof(png_bytep)*size_y);

  for(i=0;i<size_y;i++) {
    row_pointers[i]=(png_bytep) img->array[i];
  }

  png_read_image(png_ptr,row_pointers);

  png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);

  flgr_free(row_pointers);

  fclose(fp);

  return img;
#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for png. you must recompile with -DLIB_PNG_SUPPORT flag!\n");
  return FLGR_RET_NOT_IMPLEMENTED;
#endif

}
//////////////////////////////////////////////////////////////////
/*! Save a Gray level Image in PNG File format
 *  @param img : FLGR_Data2D
 *  @param filename : string for filename (if no extension is given, '.png' is concatened)
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_png(FLGR_Data2D *img, char *filename) {
#ifdef LIB_PNG_SUPPORT

  FILE *fp = NULL;
  char *fname;
  png_structp png_ptr = NULL;
  png_infop info_ptr = NULL;
  int bitdepth, colortype;
  png_bytep *row_pointers = NULL;
  int i;

  FLGR_DECLARE_FUNCTION;
  

  if(img==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(!flgr_check_extension(filename,".png"))
    fname = flgr_set_extension(filename,".png");
  else
    fname = flgr_set_extension(filename,"");

/*   if(img->bps!=8) { */
/*     POST_ERROR("can only write 8 bits gray levels png image!\n"); */
/*     flgr_free(fname); */
/*     return FLGR_RET_PARAM_UNKNOWN; */
/*   } */

  if((fp=fopen(fname, "wb")) == NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",fname);
    flgr_free(fname);
    return FLGR_RET_ALLOCATION_ERROR;
  }

  png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  if (!png_ptr) {
    POST_ERROR("Could not create write structure\n");
    flgr_free(fname);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  info_ptr = png_create_info_struct( png_ptr );
  if (!info_ptr) {
    POST_ERROR("Could not create info structure\n");
    flgr_free(fname);
    png_destroy_write_struct(&png_ptr, NULL);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    POST_ERROR("Error during writing header\n");
    flgr_free(fname);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  /* setup the output control */
  png_init_io( png_ptr, fp );

  png_set_compression_level(png_ptr, Z_NO_COMPRESSION);

  /* prepare variables needed to create PNG header */
  switch(img->type) {
  case FLGR_UINT8: bitdepth = 8; break;
  case FLGR_UINT16: bitdepth = 16; break;
  case FLGR_UINT32: bitdepth = 32; break;
  case FLGR_INT8: bitdepth = 8; break;
  case FLGR_INT16: bitdepth = 16;break;
  case FLGR_INT32: bitdepth = 32;break;
  default:
    POST_ERROR("Unsupported image type\n");
    flgr_free(fname);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  switch(img->spp) {
  case 1: colortype = PNG_COLOR_TYPE_GRAY; break;
  case 3: colortype = PNG_COLOR_TYPE_RGB; break;
  case 4: colortype = PNG_COLOR_TYPE_RGB_ALPHA; break;
  default:
    POST_ERROR("Unsupported pixel sample format\n");
    flgr_free(fname);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }
     

  /* create the PNG header */
  png_set_IHDR( png_ptr, info_ptr, img->size_x, img->size_y, bitdepth,
                colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
                PNG_FILTER_TYPE_DEFAULT );


  if (setjmp(png_jmpbuf(png_ptr))) {
    POST_ERROR("Error during writing bytes\n");
    png_destroy_write_struct(&png_ptr, &info_ptr);
    flgr_free(fname);
    fclose(fp);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  png_write_info( png_ptr, info_ptr );

  row_pointers = (png_bytep*) flgr_malloc(sizeof(png_bytep)*img->size_y);

  for(i=0;i<img->size_y;i++) {
    row_pointers[i]=(png_bytep) img->array[i];
  }

  png_write_image(png_ptr,row_pointers);
  png_write_end(png_ptr, NULL);

  flgr_free(row_pointers);
  flgr_free(fname);
  png_destroy_write_struct(&png_ptr,(png_infopp)NULL);
  fclose(fp);

  return FLGR_RET_OK;

#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for png. you must recompile with -DLIB_PNG_SUPPORT flag!\n");
  return FLGR_RET_NOT_IMPLEMENTED;
#endif

}





/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** JPEG SUPPORT LOAD/SAVE
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/

#ifdef LIB_JPEG_SUPPORT

struct my_error_mgr {
  struct jpeg_error_mgr pub;
  jmp_buf setjmp_buffer;
};

typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void) my_error_exit(j_common_ptr cinfo) {
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

#endif



//////////////////////////////////////////////////////////////////
/*! Load a JPEG Image File
 *  @param filename : String for png file name
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_jpeg(char *filename) {
#ifdef LIB_JPEG_SUPPORT

  struct jpeg_decompress_struct cinfo;
  struct my_error_mgr jerr;
  
  unsigned char *imgbuffer;
  JSAMPROW buffer[1];		/* pointer to a single row */
  int row_stride;		/* physical row width in output buffer */

  FLGR_Data2D *img = NULL;
  FILE *infile = NULL;

  FLGR_DECLARE_FUNCTION;
  

  /* open image file */
  infile = fopen (filename, "rb");
  if (!infile) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    POST_ERROR("Decompress Error\n");
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return NULL;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, infile);
  jpeg_read_header(&cinfo, TRUE);
  jpeg_start_decompress(&cinfo);

  row_stride = cinfo.output_width * cinfo.output_components;

  imgbuffer = flgr_malloc(row_stride * cinfo.output_height);
  buffer[0] = imgbuffer;

  while (cinfo.output_scanline < cinfo.output_height) {
    jpeg_read_scanlines(&cinfo, buffer, 1);
    buffer[0] += row_stride;
  }

  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);


  if((cinfo.output_components == 1) && (cinfo.out_color_space == JCS_GRAYSCALE)) {
    img = flgr2d_create_pixmap(cinfo.output_height, cinfo.output_width, 1, FLGR_UINT8);
    flgr2d_import_raw_ptr(img,imgbuffer);

  }else if ((cinfo.output_components == 3) && (cinfo.out_color_space == JCS_RGB)) {
    img = flgr2d_create_pixmap(cinfo.output_height, cinfo.output_width, 3, FLGR_UINT8);
    flgr2d_import_raw_ptr(img,imgbuffer);

  } else  {
    POST_ERROR("Support only 8 bits gray and 24 bits RGB format\n");
  }
    
  fclose(infile);
  flgr_free(imgbuffer);
  return img;
  
#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for jpeg. you must recompile with -DLIB_JPEG_SUPPORT flag!\n");
  return NULL;
#endif
}



//////////////////////////////////////////////////////////////////
/*! Save Image to JPEG File format
 *  @param img : FLGR_Data2D
 *  @param quality : Value in [0;100]
 *  @param filename : string for filename
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_jpeg(FLGR_Data2D *img, char *filename, int quality) {
#ifdef LIB_JPEG_SUPPORT
  struct jpeg_compress_struct cinfo;
  struct my_error_mgr jerr;

  /* More stuff */
  FILE * outfile;		/* target file */
  char *fname;
  FLGR_Data1D *signal;
  int row_stride;
  
  FLGR_DECLARE_FUNCTION;
  
  if(img->type != FLGR_UINT8) return FLGR_RET_NOT_IMPLEMENTED;

  /* Step 1: allocate and initialize JPEG compression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    POST_ERROR("Decompress Error\n");
    jpeg_destroy_compress(&cinfo);
    return FLGR_RET_UNDEFINED_ERROR;
  }

  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);


  if(!flgr_check_extension(filename,".jpeg"))
    fname = flgr_set_extension(filename,".jpeg");
  else
    fname = flgr_set_extension(filename,"");

  if ((outfile = fopen(fname, "wb")) == NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",fname);
    jpeg_destroy_compress(&cinfo);
    flgr_free(fname);
    return FLGR_RET_ALLOCATION_ERROR;
  }
  jpeg_stdio_dest(&cinfo, outfile);

  cinfo.image_width = img->size_x; 
  cinfo.image_height = img->size_y;

  signal = (FLGR_Data1D *) img->row;

  switch(img->spp) {
  case 1: cinfo.in_color_space = JCS_GRAYSCALE; break;
  case 3: cinfo.in_color_space = JCS_RGB; break;
  default:
    POST_ERROR("Support only 8 bits gray scale and 24 Bits RGB images\n");
    jpeg_destroy_compress(&cinfo);
    flgr_free(fname);
    fclose(outfile); 
    return FLGR_RET_NOT_IMPLEMENTED;
  }

  cinfo.input_components = img->spp;
  jpeg_set_defaults(&cinfo);
  jpeg_set_quality(&cinfo, quality, TRUE);
  jpeg_start_compress(&cinfo, TRUE);
  row_stride = img->spp*img->size_x;
  jpeg_write_scanlines(&cinfo, (JSAMPLE **) img->array, cinfo.image_height);
 
  jpeg_finish_compress(&cinfo);

  fclose(outfile);

  jpeg_destroy_compress(&cinfo);


  return FLGR_RET_OK;
#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for jpeg. you must recompile with -DLIB_JPEG_SUPPORT flag!\n");
  return FLGR_RET_NOT_IMPLEMENTED;
#endif
}




/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** TIFF SUPPORT LOAD/SAVE
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/

//////////////////////////////////////////////////////////////////
/*! Get Tiff Image Informations
 *  @param filename : String for png file name
 *  @returns a string
 */
//////////////////////////////////////////////////////////////////
char *flgr2d_load_tiff_info(char *filename) {
#ifdef LIB_TIFF_SUPPORT
  TIFF *image;
  fgUINT16 photo=0, bps=0, spp=0,compression=0, fillorder=FILLORDER_MSB2LSB;
  fgUINT16 orientation=0,planarconfig=0;
  fgUINT32 width=0,height=0;
  char *documentname, *description,*make,*model;
  int fileBigEndian;

  char info_orientation[128];
  char info_width[128];
  char info_height[128];
  char info_bps[128];
  char info_spp[128];
  char info_photo[128];
  char info_planarconfig[128];
  char info_fillorder[128];
  char info_compression[128];
  char info_bigendian[128];
  char info_hardendian[128];
  char info_documentname[1024];
  char info_description[1024];
  char info_make[1024];
  char info_model[1024];

  char *info;

  FLGR_DECLARE_FUNCTION;
  
  // Open the TIFF image
  if((image = TIFFOpen(filename, "r")) == NULL){
    POST_ERROR("Could not open %s\n",filename);
    return NULL;
  }

  TIFFGetField(image, TIFFTAG_IMAGEWIDTH,       &width);
  TIFFGetField(image, TIFFTAG_IMAGELENGTH,      &height);
  TIFFGetField(image, TIFFTAG_BITSPERSAMPLE,    &bps);
  TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL,  &spp);
  TIFFGetField(image, TIFFTAG_PHOTOMETRIC,      &photo);
  TIFFGetField(image, TIFFTAG_FILLORDER,        &fillorder);
  TIFFGetField(image, TIFFTAG_COMPRESSION,      &compression);
  TIFFGetField(image, TIFFTAG_DOCUMENTNAME,     &documentname);
  TIFFGetField(image, TIFFTAG_IMAGEDESCRIPTION, &description);
  TIFFGetField(image, TIFFTAG_MAKE,             &make);
  TIFFGetField(image, TIFFTAG_MODEL,            &model);
  TIFFGetField(image, TIFFTAG_ORIENTATION,      &orientation);
  TIFFGetField(image, TIFFTAG_PLANARCONFIG,     &planarconfig);
  fileBigEndian = TIFFIsBigEndian(image);

  sprintf(info_width,"# Tiff Width : %d\n",width);
  sprintf(info_height,"# Tiff Height : %d\n",height);
  sprintf(info_bps,"# Tiff Bit Per Sample : %d\n",bps);
  sprintf(info_spp,"# Tiff Sample Per Pixel : %d\n",spp);
  sprintf(info_documentname,"# Tiff Document name : %s\n",documentname);
  sprintf(info_description,"# Tiff Description : %s\n",description);
  sprintf(info_make,"# Tiff Scanner manufacturer name : %s\n",make);
  sprintf(info_model,"# Tiff Scanner model name/number : %s\n",model);

  switch(planarconfig) {
  case PLANARCONFIG_CONTIG:
    strcpy(info_planarconfig,"# Tiff Planar Configuration : single image plane\n");
    break;
  case PLANARCONFIG_SEPARATE:
    strcpy(info_planarconfig,"# Tiff Planar Configuration : separate planes of data\n");
    break;
  default:
    strcpy(info_planarconfig,"# Tiff Planar Configuration : **unknown**\n");
  }

  switch(orientation) {
  case ORIENTATION_TOPLEFT:
    strcpy(info_orientation,"# Tiff Orientation : Top Left\n");
    break;
  case ORIENTATION_TOPRIGHT:
    strcpy(info_orientation,"# Tiff Orientation : Top Right\n");
    break;
  case ORIENTATION_BOTRIGHT:
    strcpy(info_orientation,"# Tiff Orientation : Bottom Right\n");
    break;
  case ORIENTATION_BOTLEFT:
    strcpy(info_orientation,"# Tiff Orientation : Bottom Left\n");
    break;
  case ORIENTATION_LEFTTOP:
    strcpy(info_orientation,"# Tiff Orientation : Left Top\n");
    break;
  case ORIENTATION_RIGHTTOP:
    strcpy(info_orientation,"# Tiff Orientation : Right Top\n");
    break;
  case ORIENTATION_RIGHTBOT:
    strcpy(info_orientation,"# Tiff Orientation : Right Bottom\n");
    break;
  case ORIENTATION_LEFTBOT:
    strcpy(info_orientation,"# Tiff Orientation : Left Bottom\n");
    break;
  default:
    strcpy(info_orientation,"# Tiff Orientation : **unknown**\n");
  }

  switch(photo) {
  case PHOTOMETRIC_MINISWHITE: 
    strcpy(info_photo,"# Tiff Photometric : min value is white\n");
    break;
  case PHOTOMETRIC_MINISBLACK: 
    strcpy(info_photo,"# Tiff Photometric : min value is black\n");
    break;
  case PHOTOMETRIC_RGB:        
    strcpy(info_photo,"# Tiff Photometric : RGB color model\n");
    break;
  case PHOTOMETRIC_PALETTE:    
    strcpy(info_photo,"# Tiff Photometric : color map indexed\n");
    break;
  case PHOTOMETRIC_MASK:       
    strcpy(info_photo,"# Tiff Photometric : $holdout mask\n");
    break;
  case PHOTOMETRIC_SEPARATED:  
    strcpy(info_photo,"# Tiff Photometric : !color separations\n");
    break;
  case PHOTOMETRIC_YCBCR:      
    strcpy(info_photo,"# Tiff Photometric : !CCIR 601\n"); 
    break;
  case PHOTOMETRIC_CIELAB:     
    strcpy(info_photo,"# Tiff Photometric : !1976 CIE L*a*b*\n"); 
    break;
  case PHOTOMETRIC_ICCLAB:     
    strcpy(info_photo,"# Tiff Photometric : ICC L*a*b* [Adobe TIFF Technote 4]\n");
    break;
  case PHOTOMETRIC_ITULAB:     
    strcpy(info_photo,"# Tiff Photometric : ITU L*a*b*\n");
    break;
  case PHOTOMETRIC_LOGL:       
    strcpy(info_photo,"# Tiff Photometric : CIE Log2(L)\n"); 
    break;
  case PHOTOMETRIC_LOGLUV:
    strcpy(info_photo,"# Tiff Photometric : CIE Log2(L) (u',v')\n"); 
    break;
  default:                     
    strcpy(info_photo,"# Tiff Photometric : **unknown**\n"); 
  }    

  switch(compression) {
  case COMPRESSION_NONE:          
    strcpy(info_compression,"# Tiff Compression : dump mode\n"); 
    break;
  case COMPRESSION_CCITTRLE:      
    strcpy(info_compression,"# Tiff Compression : CCITT modified Huffman RLE\n"); 
    break;
  case COMPRESSION_CCITTFAX3:     
    strcpy(info_compression,"# Tiff Compression : CCITT Group 3 fax encoding\n"); 
    break;
  case COMPRESSION_CCITTFAX4:     
    strcpy(info_compression,"# Tiff Compression : CCITT Group 4 fax encoding\n"); 
    break;
  case COMPRESSION_LZW:           
    strcpy(info_compression,"# Tiff Compression : Lempel-Ziv  & Welch\n"); 
    break;
  case COMPRESSION_OJPEG:         
    strcpy(info_compression,"# Tiff Compression : !6.0 JPEG\n"); 
    break;
  case COMPRESSION_JPEG:          
    strcpy(info_compression,"# Tiff Compression : %JPEG DCT compression\n"); 
    break;
  case COMPRESSION_NEXT:          
    strcpy(info_compression,"# Tiff Compression : NeXT 2-bit RLE\n"); 
    break;
  case COMPRESSION_CCITTRLEW:     
    strcpy(info_compression,"# Tiff Compression : #1 w/ word alignment\n"); 
    break;
  case COMPRESSION_PACKBITS:      
    strcpy(info_compression,"# Tiff Compression : Macintosh RLE\n"); 
    break;
  case COMPRESSION_THUNDERSCAN:   
    strcpy(info_compression,"# Tiff Compression : ThunderScan RLE\n"); 
    break;
  case COMPRESSION_IT8CTPAD:      
    strcpy(info_compression,"# Tiff Compression : IT8 CT w/padding\n"); 
    break;
  case COMPRESSION_IT8LW:         
    strcpy(info_compression,"# Tiff Compression : IT8 Linework RLE\n"); 
    break;
  case COMPRESSION_IT8MP:         
    strcpy(info_compression,"# Tiff Compression : IT8 Monochrome picture\n"); 
    break;
  case COMPRESSION_IT8BL:         
    strcpy(info_compression,"# Tiff Compression : IT8 Binary line art\n"); 
    break;
  case COMPRESSION_PIXARFILM:     
    strcpy(info_compression,"# Tiff Compression : Pixar companded 10bit LZW\n"); 
    break;
  case COMPRESSION_PIXARLOG:      
    strcpy(info_compression,"# Tiff Compression : Pixar companded 11bit ZIP\n"); 
    break;
  case COMPRESSION_DEFLATE:       
    strcpy(info_compression,"# Tiff Compression : Deflate compression\n"); 
    break;
  case COMPRESSION_ADOBE_DEFLATE: 
    strcpy(info_compression,"# Tiff Compression : Deflate compression Adobe\n"); 
    break;
  case COMPRESSION_DCS:           
    strcpy(info_compression,"# Tiff Compression : Kodak DCS encoding\n"); 
    break;
  case COMPRESSION_JBIG:          
    strcpy(info_compression,"# Tiff Compression : ISO JBIG\n"); 
    break;
  case COMPRESSION_SGILOG:        
    strcpy(info_compression,"# Tiff Compression : SGI Log Luminance RLE\n"); 
    break;
  case COMPRESSION_SGILOG24:      
    strcpy(info_compression,"# Tiff Compression : SGI Log 24-bit packed\n"); 
    break;
  case COMPRESSION_JP2000:
    strcpy(info_compression,"# Tiff Compression : Leadtools JPEG2000\n"); 
    break;
  default:      
    strcpy(info_compression,"# Tiff Compression : **unknown**\n"); 
  }

  switch(fillorder) {
  case FILLORDER_MSB2LSB: strcpy(info_fillorder,"# Tiff Fill Order : MSB downto LSB\n"); break;
  default:                strcpy(info_fillorder,"# Tiff Fill Order : LSB to MSB\n"); 
  }

  if(fileBigEndian) {
    strcpy(info_bigendian,"# Tiff Data : Big Endian\n");
  }else{
    strcpy(info_bigendian,"# Tiff Data : Little Endian\n"); 
  }

  if(flgr_system_is_little_endian()) {
    strcpy(info_hardendian,"# System : Little Endian Hardware\n");
  }else{
    strcpy(info_hardendian,"# System : Big Endian Hardware\n"); 
  }

  info = flgr_malloc((strlen(info_hardendian)+strlen(info_bigendian)+strlen(info_fillorder)+
		      strlen(info_compression)+strlen(info_photo)+strlen(info_spp)+strlen(info_bps)+
		      strlen(info_height)+strlen(info_width)+strlen(info_documentname)+
		      strlen(info_description)+strlen(info_make)+strlen(info_model)+
		      strlen(info_orientation)+strlen(info_planarconfig)+1)*sizeof(char));

  strcpy(info,info_documentname);
  strcat(info,info_description);
  strcat(info,info_make);
  strcat(info,info_model);
  strcat(info,info_orientation);
  strcat(info,info_planarconfig);
  strcat(info,info_width);
  strcat(info,info_height);
  strcat(info,info_bps);  
  strcat(info,info_spp);  
  strcat(info,info_photo);
  strcat(info,info_compression);  
  strcat(info,info_fillorder);
  strcat(info,info_bigendian);  
  strcat(info,info_hardendian);

  TIFFClose(image);

  return info;

#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for tiff. you must recompile with -DLIB_TIFF_SUPPORT flag!\n");
  return NULL;
#endif
}


//////////////////////////////////////////////////////////////////
/*! Load a Tiff Image File
 *  @param filename : String for png file name
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_tiff(char *filename) {
#ifdef LIB_TIFF_SUPPORT
  TIFF *image;
  //int isLittleEndian = flgr_system_is_little_endian();
  int fileBigEndian;
  fgUINT16 photo=0, bps=0, spp=0, fillorder=FILLORDER_MSB2LSB;
  fgUINT32 width=0,height=0;
  FLGR_Data2D *img = NULL;

  FLGR_DECLARE_FUNCTION;
  
  // Open the TIFF image
  if((image = TIFFOpen(filename, "r")) == NULL){
    POST_ERROR("Could not open %s\n",filename);
    return NULL;
  }

  TIFFGetField(image, TIFFTAG_IMAGEWIDTH,      &width);
  TIFFGetField(image, TIFFTAG_IMAGELENGTH,     &height);
  TIFFGetField(image, TIFFTAG_BITSPERSAMPLE,   &bps);
  TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);
  TIFFGetField(image, TIFFTAG_PHOTOMETRIC,     &photo);
  TIFFGetField(image, TIFFTAG_FILLORDER,       &fillorder);

  fileBigEndian = TIFFIsBigEndian(image);
  //check hardware host fill order (write a function in core/data ??)

  if( (spp==3) || (spp==4) ) {  
    if( bps <= 8 || (bps<=16 && photo==PHOTOMETRIC_RGB) ) {
      fgUINT32 *raster = flgr_malloc(width*height*4);
      
      img = flgr2d_create_pixmap(height,width,4,FLGR_UINT8);
      
      TIFFReadRGBAImage(image, width, height, raster, ORIENTATION_TOPLEFT);
      
      flgr2d_import_raw_ptr(img,raster);
      flgr2d_mirror_vertical_hmorph(img);
      
      flgr_free(raster);

    }else if (( bps <= 16 ) && (photo==PHOTOMETRIC_CIELAB)) {
      float *whitePoint;
      float refWhite[3];
      TIFFCIELabToRGB* cielab = 0;
      FLGR_Vector *vec = flgr_vector_create(3,FLGR_UINT8);
      int i,j;
      char *scanlineBuffer;
      TIFFDisplay display_sRGB = {
	{       /* XYZ -> luminance matrix */
	  {  3.2410F, -1.5374F, -0.4986F },
	  {  -0.9692F, 1.8760F, 0.0416F },
	  {  0.0556F, -0.2040F, 1.0570F }
        },      
        100.0F, 100.0F, 100.0F, /* Light o/p for reference white */
        255, 255, 255,      /* Pixel values for ref. white */
        1.0F, 1.0F, 1.0F,   /* Residual light o/p for black pixel */
        2.4F, 2.4F, 2.4F,   /* Gamma values for the three guns */
      };

      //POST_INFO("bps <= 16 and PHOTOMETRIC_CIELAB\n");

      /* Initialize structures */
      cielab = (TIFFCIELabToRGB *) flgr_malloc(sizeof(TIFFCIELabToRGB));

      if (!cielab) {
        POST_ERROR("No space for CIE L*a*b*->RGB conversion state\n");
 	TIFFClose(image);
        return NULL;
      }

      TIFFGetFieldDefaulted(image, TIFFTAG_WHITEPOINT, &whitePoint);
      refWhite[1] = 100.0F;
      refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1];
      refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1]) / whitePoint[1] * refWhite[1];

      if (TIFFCIELabToRGBInit(cielab, &display_sRGB, refWhite) < 0) {
        POST_ERROR("Failed to initialize CIE L*a*b*->RGB conversion state\n");
        flgr_free(cielab);
	TIFFClose(image);
	return NULL;
      }

      /* Now we can start to convert */
      img = flgr2d_create_pixmap(height,width,3,FLGR_UINT8);

      scanlineBuffer = flgr_malloc(TIFFScanlineSize(image));

      for(i=0 ; i<height ; i++) {
	fgINT16* p;

	TIFFReadScanline(image, scanlineBuffer, i,0);
	p = (fgINT16*)scanlineBuffer; // a and b are signed, L is not...

	for(j=0 ; j<width ; j++,p+=spp) {
	  fgFLOAT32 X, Y, Z;
	  fgUINT32 r, g, b;

	  TIFFCIELabToXYZ(cielab, ((fgUINT16) (p[0]))>>8, p[1]>>8, p[2]>>8, &X, &Y, &Z);
	  TIFFXYZToRGB(cielab, X, Y, Z, &r, &g, &b);

	  flgr_vector_set_element_fgUINT8(vec,0,(fgUINT8) r);
	  flgr_vector_set_element_fgUINT8(vec,1,(fgUINT8) g);
	  flgr_vector_set_element_fgUINT8(vec,2,(fgUINT8) b);

	  flgr2d_set_data_vector_fgUINT8(img,i,j,vec);

	}
      }
		 

      /* Don't forget to free the state structure */
      flgr_free(scanlineBuffer);
      flgr_free(cielab);
      flgr_vector_destroy(vec);
    }
  }

  TIFFClose(image);
  return img;

#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for tiff. you must recompile with -DLIB_TIFF_SUPPORT flag!\n");
  return NULL;
#endif
}



//////////////////////////////////////////////////////////////////
/*! Save Image to Tiff File format
 *  @param img : FLGR_Data2D
 *  @param filename : string for filename
 *  @param quality : tiff quality
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_tiff(FLGR_Data2D *img, char *filename, int quality) {
#ifdef LIB_TIFF_SUPPORT
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("Not supported yet\n");

  return FLGR_RET_NOT_IMPLEMENTED;

  return FLGR_RET_OK;
#else
  FLGR_DECLARE_FUNCTION;
  
  POST_ERROR("No support for tiff. you must recompile with -DLIB_TIFF_SUPPORT flag!\n");
  return FLGR_RET_NOT_IMPLEMENTED;
#endif
}



/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** RAW SUPPORT LOAD
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/

//////////////////////////////////////////////////////////////////
/*! Load a raw from a file descriptor.
 *
 *  The descriptor is not closed.
 *  
 *  @param f : file descriptor
 *  @param byte_offset : how many byte to skip (to skip header)
 *  @param size_x : image width 
 *  @param size_y : image height
 *  @param spp : how many sample per pixels
 *  @param type : data type to use
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_raw_desc(FILE *f, int byte_offset, int size_y, int size_x, int spp, FLGR_Type type) {
  FLGR_Data2D *img=NULL;
  FLGR_Data1D *img_1d_buf=NULL;
  int read_size, size;

  FLGR_DECLARE_FUNCTION;
  
  fseek(f,byte_offset,SEEK_CUR);  

  if((type==FLGR_UINT64) || (type==FLGR_INT64)) {
    POST_ERROR("Unsupported raw type\n");
    return NULL;
  }else if(type==FLGR_BIT) {
    read_size = ((((size_x*size_y*spp)>>3)+1)<<3)>>3;
    size = 1;
  }else {
    read_size = size_x*size_y;
    size = spp;
  }
 
  img_1d_buf = flgr1d_create_signal(size_x*size_y,spp,type);
  img = flgr2d_create_pixmap(size_y,size_x,spp,type);

  fread(img_1d_buf->array, size, read_size, f);

  flgr1d_copy_to_2d(img, img_1d_buf);

  flgr1d_destroy(img_1d_buf);

  return img;
}



//////////////////////////////////////////////////////////////////
/*! Load a raw image File
 *  
 *  @param filename : String for raw file name
 *  @param byte_offset : how many byte to skip (to skip header)
 *  @param size_x : image width 
 *  @param size_y : image height
 *  @param spp : how many sample per pixels
 *  @param type : data type to use
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_raw_file(char *filename, int byte_offset, int size_y, int size_x, int spp, FLGR_Type type) {
  FILE *f;
  FLGR_Data2D *img;

  FLGR_DECLARE_FUNCTION;
  
  //Open File
  if( (f=fopen(filename,"rb"))==NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  img = flgr2d_load_raw_desc(f, byte_offset, size_y, size_x, spp, type);

  fclose(f);

  return img;
 
}





//////////////////////////////////////////////////////////////////
/*! sav a raw to a file descriptor.
 *
 *  @param dat : 2d data to save
 *  @param f : file descriptor
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_raw_desc(FLGR_Data2D *dat, FILE *f) {
  FLGR_Data1D *img_1d_buf=NULL;
  int read_size, size;

  FLGR_DECLARE_FUNCTION;
  
  if(dat->type==FLGR_BIT) {
    read_size = ((((dat->size_x*dat->size_y*dat->spp)>>3)+1)<<3)>>3;
    size = 1;
  }else {
    read_size = dat->size_x*dat->size_y;
    size = dat->spp;
  }
 
  img_1d_buf = flgr1d_create_signal(dat->size_x*dat->size_y,dat->spp,dat->type);
  flgr2d_copy_to_1d(img_1d_buf, dat);

  fwrite(img_1d_buf->array, size, read_size, f);

  flgr1d_destroy(img_1d_buf);

  return FLGR_RET_OK;
}



/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** STRING SUPPORT LOAD
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/

//////////////////////////////////////////////////////////////////
/*! Load a text from a file descriptor.
 *
 *  The descriptor is not closed.
 *  
 *  @param f : file descriptor
 *  @param size_x : image width 
 *  @param size_y : image height
 *  @param spp : how many sample per pixels
 *  @param type : data type to use
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_text_desc(FILE *f, int size_y, int size_x, int spp, FLGR_Type type) {
  FLGR_Data2D *img=NULL;
  FLGR_Vector *vec;
  int i,j,k;
  

  FLGR_DECLARE_FUNCTION;
  


  if((type==FLGR_UINT64) || (type==FLGR_INT64)) {
    POST_ERROR("Unsupported file type\n");
    return NULL;
  }
 
  vec = flgr_vector_create(spp,type);

  img = flgr2d_create_pixmap(size_y,size_x,spp,type);

  for(i=0 ; i<size_y ; i++) {
    for(j=0 ; j<size_x ; j++) {

      for(k=0 ; k<spp ; k++) {
	if(type == FLGR_BIT) {
	  fgUINT32 val;
	  fscanf(f,"%u",&val); flgr_vector_set_element_fgUINT32(vec, k, ((fgBIT) val)&1);
	}else if(type == FLGR_UINT8) {
	  fgUINT32 val;
	  fscanf(f,"%u",&val); flgr_vector_set_element_fgUINT32(vec, k, (fgUINT8) val);
	}else if(type == FLGR_UINT16) {
	  fgUINT32 val;
	  fscanf(f,"%u",&val); flgr_vector_set_element_fgUINT32(vec, k, (fgUINT16) val);
	}else if(type == FLGR_UINT32) {
	  fgUINT32 val;
	  fscanf(f,"%u",&val); flgr_vector_set_element_fgUINT32(vec, k, val);
	}else if(type == FLGR_INT8) {
	  fgINT32 val;
	  fscanf(f,"%d",&val); flgr_vector_set_element_fgINT8(vec, k, (fgINT8) val);
	}else if(type == FLGR_INT16) {
	  fgINT32 val;
	  fscanf(f,"%d",&val); flgr_vector_set_element_fgINT16(vec, k, (fgINT16) val);
	}else if(type == FLGR_INT32) {
	  fgINT32 val;
	  fscanf(f,"%d",&val); flgr_vector_set_element_fgINT32(vec, k, val);
	}else if(type == FLGR_FLOAT32) {
	  fgFLOAT32 val;
	  fscanf(f,"%g",&val); flgr_vector_set_element_fgFLOAT32(vec, k, val);
	}else {
	  fgFLOAT64 val;
	  fscanf(f,"%lg",&val); flgr_vector_set_element_fgFLOAT64(vec, k, val);
	}
      }

      flgr2d_set_data_vector(img,i,j,vec);

    }
  }

  flgr_vector_destroy(vec);

  return img;
}



//////////////////////////////////////////////////////////////////
/*! Load a text file and copy values to an images
 *  
 *  @param filename : String for raw file name
 *  @param size_x : image width 
 *  @param size_y : image height
 *  @param spp : how many sample per pixels
 *  @param type : data type to use
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_text_file(char *filename, int size_y, int size_x, int spp, FLGR_Type type) {
  FILE *f;
  FLGR_Data2D *img;

  FLGR_DECLARE_FUNCTION;
  
  //Open File
  if( (f=fopen(filename,"rt"))==NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  img = flgr2d_load_text_desc(f, size_y, size_x, spp, type);

  fclose(f);

  return img;
 
}





//////////////////////////////////////////////////////////////////
/*! save an image to a text file
 *
 *  @param dat : 2d data to save
 *  @param f : file descriptor
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_text_desc(FLGR_Data2D *img, FILE *f) {
  FLGR_Vector *vec;
  FLGR_Type type = img->type;
  int spp = img->spp;
  int size_x = img->size_x;
  int size_y = img->size_y;
  int i,j,k;
  

  FLGR_DECLARE_FUNCTION;
  

  vec = flgr_vector_create(spp,type);


  for(i=0 ; i<size_y ; i++) {
    for(j=0 ; j<size_x ; j++) {
      flgr2d_get_data_vector(img,i,j,vec);

      for(k=0 ; k<spp ; k++) {
	if(type == FLGR_BIT) {
	  fgBIT val;
	  flgr_vector_get_element_fgBIT(vec, k, &val );
	  fprintf(f,"%u\n",(unsigned int) val); 

	}else if(type == FLGR_UINT8) {
	  fgUINT8 val;
	  flgr_vector_get_element_fgUINT8(vec, k, &val ); 
	  fprintf(f,"%u\n",(unsigned int) val); 

	}else if(type == FLGR_UINT16) {
	  fgUINT16 val;
	  flgr_vector_get_element_fgUINT16(vec, k, &val);
	  fprintf(f,"%u\n",(unsigned int) val); 

	}else if(type == FLGR_UINT32) {
	  fgUINT32 val;
	  flgr_vector_get_element_fgUINT32(vec, k, &val);
	  fprintf(f,"%u\n",val); 

	}else if(type == FLGR_INT8) {
	  fgINT8 val;
	  flgr_vector_get_element_fgINT8(vec, k, &val);
	  fprintf(f,"%d\n",(int) val); 

	}else if(type == FLGR_INT16) {
	  fgINT16 val;
	  flgr_vector_get_element_fgINT16(vec, k, &val);
	  fprintf(f,"%d\n",(int) val); 

	}else if(type == FLGR_INT32) {
	  fgINT32 val;
	  flgr_vector_get_element_fgINT32(vec, k, &val);
	  fprintf(f,"%d\n",val); 

	}else if(type == FLGR_FLOAT32) {
	  fgFLOAT32 val;
	  flgr_vector_get_element_fgFLOAT32(vec, k, &val);
	  fprintf(f,"%g\n",val); 

	}else if(type == FLGR_FLOAT64) {
	  fgFLOAT64 val;
	  flgr_vector_get_element_fgFLOAT64(vec, k, &val);
	  fprintf(f,"%lg\n",val);

	}
      }

    }
  }

  flgr_vector_destroy(vec);

  return FLGR_RET_OK;
}






/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** PGM SUPPORT LOAD/SAVE
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/


//////////////////////////////////////////////////////////////////
/*! Load a Gray level PGM Image File (P2 or P5)
 *  @param filename : String for pgm file name
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_pgm(char *filename) {
  FLGR_Data2D *img=NULL;
  FILE *f;
  fpos_t fposition;
  char buff[16];
  int size_x,size_y;
  int color_range;
  int car;
  unsigned char stop;
  int isP2=0;

  FLGR_DECLARE_FUNCTION;
  
  //Open File
  if( (f=fopen(filename,"rb"))==NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  //read magic number
  fscanf(f,"%s\n",buff);
  if(strcmp(buff,"P5") == 0) {
    isP2 = 0;
  }else if(strcmp(buff,"P2") == 0) {
    isP2 = 1;
  }else{
    fclose(f);
    POST_ERROR("\"%s\" is not a valid pgm (P2 or P5) !\n",filename);
    return NULL;
  }

  //read comments
  stop=0;
  do {
    fgetpos(f, &fposition);
    car=fgetc(f);
    if(car=='#') {
      while(fgetc(f)!='\n');
      stop=0;
    }else {
      stop=1;
    }
  }while(stop==0);
  fseek(f,-2,SEEK_CUR);
  //fsetpos(f, &fposition);

  //get image size
  fscanf(f,"%d %d\n", &size_x, &size_y);

  fscanf(f,"%d\n",&color_range);

  if(color_range==255) {
    if(isP2==1)
      img = flgr2d_load_text_desc(f, size_y, size_x, 1, FLGR_UINT8);
    else
      img = flgr2d_load_raw_desc(f, 0, size_y, size_x, 1, FLGR_UINT8);

  }else if(color_range==65535) {
    if(isP2==1)
      img = flgr2d_load_text_desc(f, size_y, size_x, 1, FLGR_UINT16);
    else
      img = flgr2d_load_raw_desc(f, 0, size_y, size_x, 1, FLGR_UINT16);

  }else {
    POST_ERROR("\"%s\" is not a valid pgm (P2 or P5). Load only brut 8 and 16 bits pgm file !\n",filename);
  }


  fclose(f);

  return img;
}



//////////////////////////////////////////////////////////////////
/*! Save a Gray level Image in PGM File format
 *  @param img : FLGR_Data2D
 *  @param format : 2 for P2 or 5 for P5
 *  @param filename : string for file name  (if no extension is given, '.pgm' is concatened)
 *  @returns FLGR_RET_OK, ...
 */
//////////////////////////////////////////////////////////////////
FLGR_Ret flgr2d_save_pgm(FLGR_Data2D *img, char *filename, int format) {
  FILE *f;
  char *fname;
  int colorWidth = 0;

  FLGR_DECLARE_FUNCTION;
  
  if((format != 2) && (format != 5))
    return FLGR_RET_NOT_IMPLEMENTED;
  

  if(img==NULL) {
    POST_ERROR("Null objects!\n");
    return FLGR_RET_NULL_OBJECT;
  }

  if(img->spp!=1) return FLGR_RET_NOT_IMPLEMENTED;

  if(!flgr_check_extension(filename,".pgm")) {
    fname = flgr_set_extension(filename,".pgm");
  } else {
    fname = flgr_set_extension(filename,"");
  }


  if(img->type == FLGR_UINT8){
    colorWidth = 255;
  }else if(img->type == FLGR_UINT16) {
    colorWidth = 65535;
  }else {
    return FLGR_RET_NOT_IMPLEMENTED;
  }


  if((f=fopen(fname,"wb"))==NULL) {
    POST_ERROR("\"%s\" can not be created!\n",fname);
    free(fname);
    return FLGR_RET_ALLOCATION_ERROR;
  }

  if(format==5) {
    fprintf(f,"P5\n# fulguro by Christophe Clienti\n%d %d\n%d\n",img->size_x,img->size_y,colorWidth);
    flgr2d_save_raw_desc(img,f);
  }else {
    fprintf(f,"P2\n# fulguro by Christophe Clienti\n%d %d\n%d\n",img->size_x,img->size_y,colorWidth);
    flgr2d_save_text_desc(img,f);
  }

  fclose(f);
  free(fname);

  return FLGR_RET_OK;
}









/*************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************
 *********** BMP SUPPORT LOAD/SAVE
 *************************************************************************************************
 *************************************************************************************************
 *************************************************************************************************/


//////////////////////////////////////////////////////////////////
/*! Load a BMP Image File
 *  @param filename : String for bmp file name
 *  @returns a pointer to FLGR_Data2D
 */
//////////////////////////////////////////////////////////////////
FLGR_Data2D *flgr2d_load_bmp(char *filename) {
  FLGR_Data2D *img;
  FILE *f;
  char buff[16];
  unsigned int offset;
  unsigned int size_x,size_y;
  unsigned short bps;
  unsigned int compression;
  int spp;
  FLGR_Type type;

  FLGR_DECLARE_FUNCTION;
  
  //Open File
  if( (f=fopen(filename,"rb"))==NULL) {
    POST_ERROR("couldn't open \"%s\"!\n",filename);
    return NULL;
  }

  //read BM
  fread(buff,1,2,f);
  if((buff[0] != 'B') && (buff[1] != 'M')) {
    fclose(f);
    return NULL;
  }

  fread(buff,1,8,f); //skip
  fread(&offset,4,1,f);
  fread(buff,1,4,f); //skip

  fread(&size_x,4,1,f);
  fread(&size_y,4,1,f);
  fread(buff,1,2,f); //skip

  fread(&bps,2,1,f);
  fread(&compression,4,1,f);


  if(compression != 0) {
    POST_ERROR("could not open compressed bitmap\n");
    fclose(f);
    return NULL;
  }

  rewind(f);

  switch(bps) {
  case 1:         spp=1; type = FLGR_BIT;   break;
  case 8:         spp=1; type = FLGR_UINT8; break;
  case 16: bps=8; spp=2; type = FLGR_UINT8; break;
  case 24: bps=8; spp=3; type = FLGR_UINT8; break;
  case 32: bps=8; spp=4; type = FLGR_UINT8; break;
  default:
    POST_ERROR("Unsupported bitmap file\n");
    fclose(f);
    return NULL;
  }

  img = flgr2d_load_raw_desc(f, offset, size_y, size_x, spp, type);

  flgr2d_mirror_vertical_hmorph(img);

  flgr2d_revert_channel_hmorph(img);

  fclose(f);

  return img;
}







//! @}
